package com.cybertron.am.wordpress.util;

import com.ne.boot.common.exception.NEException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author xiezhouyan
 */
@Slf4j
public class HttpUtil {

    public static final OkHttpClient CLIENT;
    private static final Integer TIMEOUT = 3 * 60;

    static {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(TIMEOUT, TimeUnit.SECONDS);
        String proxy = System.getProperty("proxy");
        if (StringUtils.isNotBlank(proxy)) {
            ProxyInfo proxyInfo = ProxyUtil.createProxyInfo(proxy);
            builder.proxy(ProxyUtil.createProxy(proxyInfo));
        }
        CLIENT = builder.build();
    }

    public static InputStream download(String url) {
        return download(url, null);
    }

    public static InputStream download(String url, Map<String, String> headers) {
        Request.Builder builder = new Request.Builder()
                .url(url)
                .get();
        if (MapUtils.isNotEmpty(headers)) {
            builder.headers(Headers.of(headers));
        }
        Response response = execute(builder.build());
        ResponseBody body = response.body();
        return body.byteStream();
    }

    public static String get(String url) {
        return get(url, null, (response, content) -> content);
    }

    public static <T> T get(String url, Map<String, String> headers, Parser<T> parser) {
        Request.Builder builder = new Request.Builder().url(url).get();
        return execute(builder, headers, parser);
    }

    public static <T> T post(String url, String body, okhttp3.MediaType mediaType, Parser<T> parser) {
        return post(url, body, mediaType, null, parser);
    }

    public static <T> T post(String url, String body, okhttp3.MediaType mediaType, Map<String, String> headers, Parser<T> parser) {
        RequestBody requestBody = RequestBody.create(mediaType, body);
        Request.Builder builder = new Request.Builder().url(url).post(requestBody);
        return execute(builder, headers, parser);
    }

    public static <T> T execute(Request.Builder builder, Map<String, String> headers, Parser<T> parser) {
        if (headers != null && !headers.isEmpty()) {
            builder.headers(Headers.of(headers));
        }
        return execute(builder, parser);
    }

    public static <T> T execute(Request.Builder builder, Parser<T> parser) {
        Request request = builder.build();
        try {
            Response response = execute(request);
            if (response.code() > 400) {
                String msg = String.format("%s:%s  response status code:%s:%s:%s",
                        request.method(),
                        request.url(),
                        response.code(),
                        response.message(),
                        response.body().string());
                log.error("read response error " + msg);
                throw new NEException(msg);
            }
            String body = response.body().string();
            return parser.parse(response, body);
        } catch (Throwable e) {
            log.error("read response error", e);
            throw new NEException(e);
        }
    }

    public static ExecutorBuilder builder() {
        return new ExecutorBuilder();
    }

    public static Response execute(Request request) {
        try {
            Response response = CLIENT.newCall(request).execute();
            return response;
        } catch (Throwable e) {
            log.error("unable to connect to " + request.url(), e);
            throw new NEException("unable to connect to " + request.url(), e);
        }
    }


    public static class ExecutorBuilder {
        private Request.Builder request;
        private Parser parser;

        public ExecutorBuilder() {
            this.request = new Request.Builder();
            this.parser = (response, content) -> content;
        }

        public ExecutorBuilder url(String url) {
            request.url(url);
            return this;
        }

        public ExecutorBuilder headers(Map<String, String> headers) {
            request.headers(Headers.of(headers));
            return this;
        }

        public ExecutorBuilder parser(Parser parser) {
            this.parser = parser;
            return this;
        }

        public <T> T post(okhttp3.MediaType mediaType, String body) {
            RequestBody requestBody = RequestBody.create(mediaType, body);
            request.post(requestBody);
            return (T) HttpUtil.execute(request, parser);
        }

        public <T> T put(okhttp3.MediaType mediaType, String body) {
            RequestBody requestBody = RequestBody.create(mediaType, body);
            request.put(requestBody);
            return (T) HttpUtil.execute(request, parser);
        }

        public <T> T get() {
            request.get();
            return (T) HttpUtil.execute(request, parser);
        }

        public <T> T delete() {
            request.delete();
            return (T) HttpUtil.execute(request, parser);
        }

        public <T> T delete(okhttp3.MediaType mediaType, String body) {
            RequestBody requestBody = RequestBody.create(mediaType, body);
            request.delete(requestBody);
            return (T) HttpUtil.execute(request, parser);
        }
    }

    public interface Parser<T> {
        T parse(Response response, String content);
    }

    public static class MediaType {
        public static okhttp3.MediaType JSON = okhttp3.MediaType.parse(org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE);
        public static okhttp3.MediaType FORM = okhttp3.MediaType.parse(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        public static okhttp3.MediaType XML = okhttp3.MediaType.parse(org.springframework.http.MediaType.APPLICATION_XML_VALUE);
    }
}
